
#ifdef NO_FILE_MAP
  #include <stdio.h>
  #include <errno.h>
#elif defined(_WIN32)
  #include <windows.h>
#else
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <errno.h>
#endif

#include "alloc.h"
#include <string.h>
#include "input.h"
#define LOG_TAG "Input"
#include "log.h"

/********************** FileInput ****************************/

typedef struct {
    InputEty in;
    void *file;
    void *base;
    size_t size;
} *FileInput, FileInputEty;

#ifdef NO_FILE_MAP
Input InputFromFile(const char *path)
{
    FileInput fp = malloc_ct(FileInputEty, 1);
    if (fp == NULL) {
        LOGE("Memory exhausted");
        return NULL;
    }

    // 使用C语言标准库函数加载代码
    fp->file = fopen(path, "rb");
    if (fp->file == NULL) {
        LOGE("%s: %s", strerror(errno), path);
        return NULL;
    }

    // 获得文件的大小
    fseek(fp->file, 0, SEEK_END);
    fp->size = ftell(fp->file);
    fp->base = malloc_ct(char, fp->size);
    if (fp->base == NULL) {
        LOGE("Memory exhausted");
        return NULL;
    }

    // 读取文件全部内容
    fseek(fp->file, 0, SEEK_SET);
    fp->size = fread(fp->base, 1, fp->size, fp->file);
    fclose(fp->file);

    fp->in.ptr = fp->base;
    fp->in.begin = 0;
    fp->in.end = fp->size;
    return &fp->in;
}

void InputClose(Input in)
{
    if (in == NULL) return;
    FileInput fp = (FileInput) in;
    free_ct(fp->base);
    free_ct(fp);
}

#elif defined(_WIN32)
Input InputFromFile(const char *path)
{
    FileInput fp = malloc_ct(FileInputEty, 1);
    if (fp == NULL) {
        LOGE("Memory exhausted");
        return NULL;
    }

    // 使用Win32 API,通过内存映射加载代码
    fp->file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fp->file == INVALID_HANDLE_VALUE) {
        LOGE("%s: %s", strerror(GetLastError()), path);
        return NULL;
    }

    fp->size = GetFileSize(fp->file, NULL);
    HANDLE fm = CreateFileMapping(fp->file, NULL, PAGE_READONLY, 0, fp->size, NULL);
    if (fm == NULL) {
        LOGE("%s: %s", strerror(GetLastError()), path);
        CloseHandle(fp->file);
        return NULL;
    }

    fp->base = MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0);
    CloseHandle(fm);
    if (fp->base == NULL) {
        LOGE("%s: %s", strerror(GetLastError()), path);
        CloseHandle(fp->file);
        return NULL;
    }

    fp->in.ptr = fp->base;
    fp->in.begin = 0;
    fp->in.end = fp->size;
    return &fp->in;
}

void InputClose(Input in)
{
    if (in == NULL) return;
    FileInput fp = (FileInput) in;
    UnmapViewOfFile(fp->base);
    CloseHandle(fp->file);
    free_ct(fp);
}

#else
Input InputFromFile(const char *path)
{
    FileInput fp = malloc_ct(FileInputEty, 1);
    if (fp == NULL) {
        LOGE("Memory exhausted");
        return NULL;
    }

    // 使用类Linux库函数,通过内存映射加载代码
    int fd = open(path, O_RDONLY);
    if (fd == -1) {
        LOGE("%s: %s", strerror(errno), path);
        return NULL;
    }
    fp->file = (void *)(intptr_t) fd;

    // 获取文件大小
    struct stat st;
    if (fstat(fd, &st) == -1) {
        LOGE("%s: %s", strerror(errno), path);
        close(fd);
        return NULL;
    }
    fp->size = st.st_size;

    fp->base = mmap(NULL, fp->size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (fp->base == MAP_FAILED) {
        LOGE("%s: %s", strerror(errno), path);
        close(fd);
        return NULL;
    }

    fp->in.ptr = fp->base;
    fp->in.begin = 0;
    fp->in.end = fp->size;
    return &fp->in;
}

void InputClose(Input in)
{
    if (in == NULL) return;
    FileInput fp = (FileInput) in;
    close((int)(intptr_t) fp->file);
    munmap(fp->base, fp->size);
    free_ct(fp);
}

#endif

/********************** Input ****************************/

void InputSkipBOM(Input in)
{
    unsigned char BOM_UTF8[4] = {0xEF, 0xBB, 0xBF, 0};
    unsigned char BOM_UTF16LE[4] = {0xFF, 0xFE, 0, 0};
    unsigned char BOM_UTF16BE[4] = {0xFE, 0xFF, 0, 0};
    unsigned char BOM_GB18030[4] = {0x84, 0x31, 0x95, 0x33};

    if (!memcmp(in->ptr, BOM_UTF8, 3)) {
        in->begin = 3;
    } else if (!memcmp(in->ptr, BOM_UTF16LE, 2)) {
        in->begin = 2;
    } else if (!memcmp(in->ptr, BOM_UTF16BE, 2)) {
        in->begin = 2;
    } else if (!memcmp(in->ptr, BOM_GB18030, 4)) {
        in->begin = 4;
    } else {
        in->begin = 0;
    }
    return;
}

void InputSkipSpace(Input in)
{
    const unsigned char *ptr = (const unsigned char*) in->ptr;
    int index = in->begin;
    while (index < in->end) {
        if (ptr[index] > ' ') {
            in->begin = index;
            return;
        }
        index++;
    }
    in->begin = in->end;
}

void Input2String(Input in, String str)
{
    str->ptr = in->ptr + in->begin;
    str->len = in->end - in->begin;
}

void String2Input(String str, Input in)
{
    in->ptr = str->ptr;
    in->begin = 0;
    in->end = str->len;
}

void StringSetBegin(String str, Input in)
{
    Input2String(in, str);
}

void StringSetEnd(String str, Input in)
{
    str->len = (int) (in->ptr + in->begin - str->ptr);
}
